'use strict'

const fs = require('fs-extra')
const path = require('path')
const mockery = require('mockery')

const ChildProcessMock = require('../fixtures/child_process')
const sign = require('../../lib/sign')
const utils = require('../../lib/utils')

describe('Sign', () => {
  const tmpDir = path.join(require('os').tmpdir(), 'electron-windows-store-cert-test')

  let passedArgs = []
  let passedProcess = []

  const cpMock = {
    spawn(_process, _args) {
      passedProcess.push(_process)
      passedArgs.push(_args)

      return new ChildProcessMock()
    }
  }

  before(() => {
    fs.ensureDirSync(tmpDir)
  })

  after(() => {
    fs.removeSync(tmpDir)
  })

  afterEach(() => {
    mockery.deregisterAll()
    passedArgs = []
    passedProcess = []
  })

  describe('signappx()', () => {
    it('should attempt to sign the current app', function (done) {
      const programMock = {
        inputDirectory: '/fakepath/to/input',
        outputDirectory: '/fakepath/to/output',
        windowsKit: '/fakepath/to/windows/kit/bin',
        packageName: 'testapp',
        devCert: 'fakepath/to/devcert.pfx'
      }

      mockery.registerMock('child_process', cpMock)

      sign.signAppx(programMock)
        .then(() => {
          const expectedScript = path.join(programMock.windowsKit, 'signtool.exe')
          const expectedPfxFile = programMock.devCert
          const expectedAppx = path.join(programMock.outputDirectory, `${programMock.packageName}.appx`)
          const expectedParams = ['sign', '-f', expectedPfxFile, '-fd', 'SHA256', '-v', expectedAppx]

          passedProcess.length.should.equal(1)
          passedProcess[0].should.equal(expectedScript)
          passedArgs[0].should.deep.equal(expectedParams)
          done()
        })
    })

    it('should pass along the certificate password', function () {
      const programMock = {
        inputDirectory: '/fakepath/to/input',
        outputDirectory: '/fakepath/to/output',
        windowsKit: '/fakepath/to/windows/kit/bin',
        packageName: 'testapp',
        devCert: 'fakepath/to/devcert.p12',
        certPass: '12345'
      }

      mockery.registerMock('child_process', cpMock)

      return sign.signAppx(programMock)
        .then(() => {
          const expectedScript = path.join(programMock.windowsKit, 'signtool.exe')
          const expectedPfxFile = programMock.devCert
          const expectedAppx = path.join(programMock.outputDirectory, `${programMock.packageName}.appx`)
          const expectedParams = ['sign', '-f', expectedPfxFile, '-fd', 'SHA256', '-v', '-p', '12345', expectedAppx]

          passedProcess.length.should.equal(1)
          passedProcess[0].should.equal(expectedScript)
          passedArgs[0].should.deep.equal(expectedParams)
        })
    })

    it('should reject if no certificate is present', function () {
      const programMock = {}
      return sign.signAppx(programMock).should.be.rejected
    })
  })

  describe('makeCert()', () => {
    it('should not attempt to import certificate when install === false', function (done) {
      mockery.registerMock('child_process', cpMock)

      sign.makeCert({
        publisherName: 'CN=Test',
        certFilePath: tmpDir,
        install: false,
        program: {
          windowsKit: '/fake/kit'
        }
      })
      .then(() => {
        passedArgs.every((args) => {
          return args.every(arg => arg.indexOf('Import-PfxCertificate') === -1)
        }).should.equal(true)
        done()
      })
      .catch(() => {})
    })
  })


  describe('isValidPublisherName()', () => {
    const windowsSdkPath = process.arch === 'x64' ?
      'C:\\Program Files (x86)\\Windows Kits\\10\\bin\\x64' :
      'C:\\Program Files\\Windows Kits\\10\\bin\\x64';
    const makecertExe = path.join(windowsSdkPath, 'makecert.exe')
    const pvkFileName = path.resolve(__dirname, 'bogus-private-key.pvk');
    const skipMakeCertExecution = !fs.existsSync(makecertExe) || !fs.existsSync(pvkFileName)

    const scenarios = [{
      publisherName: ''
    }, {
      publisherName: 'CN='
    }, {
      publisherName: 'CN=-'
    }, {
      publisherName: 'cn=lower, ou=case'
    }, {
      publisherName: 'CN=first.last'
    }, {
      publisherName: 'CN="Pointlessly quoted"'
    }, {
      publisherName: 'CN=no,o=spaces'
    }, {
      publisherName: 'CN=" Leading and Trailing Spaces "'
    }, {
      publisherName: 'CN=Common Name,O=Some organization'
    }, {
      publisherName: 'O="Quoted comma, Inc."'
    }, {
      publisherName: 'CN=!@#$^&*()[]{}<>|\/.~\'-=,O="Symbols are Cool, LLC"'
    }, {
      publisherName: 'OU=Sales+CN=J. Smith,O=Multi-valued'
    }, {
      publisherName: 'CN=Duplicate+CN=Attribute'
    }, {
      publisherName: 'OU=Trailing plus+',
    }, {
      publisherName: 'CN=trailing comma,',
    }, {
      publisherName: 'CN="Escaped\\ and\\ quoted\\ spaces"'
    }, {
      publisherName: 'CN=First M Last, O="Acme, Inc."'
    }, {
      publisherName: 'CN=Marshall T. Rose, O=Dover Beach Consulting, L=Santa Clara,ST=California, C=US'
    }, {
      publisherName: 'CN=FTAM Service, CN=Bells, OU=Computer Science,\nO=University College London, C=GB'
    }, {
      publisherName: 'CN=Markus Kuhn, O=University of Erlangen, T=Mr., C=DE'
    }, {
      publisherName: 'CN=Steve Kille,\n O=ISODE Consortium,\n C=GB'
    }, {
      publisherName: 'CN=Christian Huitema; O=INRIA; C=FR'
    }, {
      publisherName: 'CN=This is a really long distinguished name: 2048 chars!........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................'
    }, {
      publisherName: 'CN=Even longer: 4096 chars 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678'
    }, {
      publisherName: 'CN=Long enough for anyone: 8192 chars 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123'
    }, {
      publisherName: 'CN=L. Eagle, O="Sue, Grabbit and Runn", C=GB'
    }, {
      publisherName: 'O=No CN'
    }, {
      publisherName: 'SERIALNUMBER=1'
    }, {
      publisherName: 'DC=A,CN=B,OU=C,O=D,STREET=123 Main St.,L=Big City,ST=Nowhere,C=XX,SN=Surname,GN=Given name,E=nobody@example.com,S=E,T=F,G=G,I=1.2.3.4'
    }, {
      publisherName: 'CN="+"',
    }, {
      publisherName: 'CN=X+'
    }, {
      publisherName: 'X',
      expectInvalid: true
    }, {
      publisherName: 'CN=X,UID=userId',
      expectInvalid: true
    }, {
      publisherName: 'CN=\ Escaped leading space"',
      expectInvalid: true
    }, {
      publisherName: 'CN="Quotation \\" Mark"',
      expectInvalid: true
    }, {
      publisherName: 'CN=X,DNQ=qualifier',
	  expectInvalid: true
    }, {
      // According to RFC1779 and RFC2243 this should be legal but MakeCert.exe does not seem to accept it
      publisherName: 'CN=Sue\\, Grabbit and Runn',
      expectInvalid: true
    }]

    scenarios.forEach((scenario) => {
      const actualResult = sign.isValidPublisherName(scenario.publisherName)
      let nameToPrint = scenario.publisherName.replace(/\n/g, '\\n')
      if (nameToPrint.length > 60)
        nameToPrint = nameToPrint.slice(0,61) + '...'

      // Compare pre-determined checks (previously confirmed with makecert.exe)
      it(`return ${scenario.expectInvalid ? 'false' : 'true'} for ${nameToPrint}`, () => {
        actualResult.should.equal(!scenario.expectInvalid, scenario.publisherName)
      })

      // Run makecert.exe and check whether or not it fails with this publisherName
      if (!skipMakeCertExecution) {
        const rnd = Date.now() + '-' + Math.floor(Math.random()*10000)
        const crtFileName = path.join(tmpDir, `makecert-${rnd}.crt`)
        const makecertArgs = ['-r', '-h', '0', '-n', scenario.publisherName, '-eku', '1.3.6.1.5.5.7.3.3', '-pe', '-sv', pvkFileName, crtFileName]

        it(`makecert.exe should ${scenario.expectInvalid ? 'fail' : 'succeed'} for ${nameToPrint}`, () => {
          return utils.executeChildProcess(makecertExe, makecertArgs)
          .then(() => { return true })
          .catch(() => { return false })
          .should.eventually.equal(!scenario.expectInvalid)
        })
      }
    })

    if (skipMakeCertExecution) {
      it.skip('should be re-tested against actual MakeCert.exe from SDK')
    }
  })
})
